package com.fate.listener;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fate.constant.OrderConstant;
import com.fate.constant.QueueConstant;
import com.fate.domain.Order;
import com.fate.domain.OrderItem;
import com.fate.feign.OrderProductFeign;
import com.fate.service.OrderItemService;
import com.fate.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class OrderListener {

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderProductFeign orderProductFeign;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 这个是处理延迟订单的监听
     * 只要有下订单
     * 这个监听肯定会执行
     * 1.拿到订单号 查询数据库 看订单到底有没有支付
     * 2.如果支付了  直接签收消息 return
     * 3.如果没有支付,还需要去支付宝确认一次 支付宝返回成功
     * 3.1修改数据库
     * 如果都没成功 就要回滚了
     * <p>
     * 场景 在30分钟的时候 支付宝回调了  但是监听里面已经查询出来了把
     * 就会出现线程安全问题
     * 1.redis解决 让修改订单状态的代码和 支付宝回调的代码 加同一把锁
     * 2.mysql的版本号
     */
    @RabbitListener(queues = QueueConstant.PROD_CHANGE_QUEUE, concurrency = "3-5")
    public void orderDeadHandler(Message message, Channel channel) {
        log.info("有订单消息进来了");
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        String orderNum = new String(message.getBody());
        System.out.println(orderNum);
        //查数据库 ...
        //加一个分布式锁 redission
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(OrderConstant.ORDER_CHANGE_PREFIX + orderNum, orderNum, 30, TimeUnit.SECONDS);
        if (!flag) {
            //加锁不成功 代码就不能往下执行
            //让这个消息  回到这个队列
            try {
                channel.basicNack(deliveryTag, false, true);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        try {


            //执行操作
            //1.查询订单表
            Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                    .eq(Order::getOrderNumber, orderNum)
            );
            //不能直接死 = 2 特别在秒杀的场景 count<0
            if (order.getStatus() != 1 && order.getStatus() != 6) {
                try {  //订单已经支付成功
                    //签收
                    channel.basicAck(deliveryTag, false);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return;
            }
            //代码走到这里面说明没有成功
            //考虑到支付的时候 自己的服务挂了 修改状态就失败了
            //TODO 查询微信或者支付宝 看那边一个确认结果

            //确认没有成功  开始 1.修改订单表状态 2.修改结算表状态 3.回滚mysql  4.回滚es库存 5.给用户发通知
            order.setStatus(6);
            order.setUpdateTime(new Date());
            //订单关闭状态
            order.setCloseType(1);
            //更新订单表
            orderService.updateById(order);
            //修改mysql库存和es
            changeStockAndEs(orderNum);
            //签收
            try {
                channel.basicAck(deliveryTag, false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }finally {
            //释放掉这个锁
            redisTemplate.delete(OrderConstant.ORDER_CHANGE_PREFIX +orderNum);
        }
    }

    private void changeStockAndEs(String orderNum) {
        //回滚mysql和es
        //prodId skuId count
        //查询orderItem
        List<OrderItem> orderItemList = orderItemService.list(new LambdaQueryWrapper<OrderItem>()
                .eq(OrderItem::getOrderNumber, orderNum)
        );
        //循环组装数据
        HashMap<String, Map<Long,Integer>> data = new HashMap<>(4);
        Map<Long,Integer>prodMap = new HashMap<>();
        HashMap<Long, Integer> skuMap = new HashMap<>();
        orderItemList.forEach(orderItem -> {
            Long prodId = orderItem.getProdId();
            Long skuId = orderItem.getSkuId();
            Integer count = orderItem.getProdCount();
            if (prodMap.containsKey(prodId)){
                prodMap.put(prodId,prodMap.get(prodId)+count);
            }else {
                prodMap.put(prodId,count);
            }
            skuMap.put(skuId,count);
        });
        data.put("prod",prodMap);
        data.put("sku",skuMap);
        //远程调用
        orderProductFeign.changeStock(data);
        //修改es库存
        rabbitTemplate.convertAndSend(QueueConstant.PROD_CHANGE_EX,QueueConstant.PROD_CHANGE_KEY, JSON.toJSONString(prodMap));
    }


}
